#include "stdafx.h"
#include "problem_solver_core.h"
#include "problem.h"
#include "solver.h"
#include "MLP.h"
#include "layer.h"
#include "input_layer.h"
#include "hidden_layer.h"
#include "output_layer.h"
#include "MLP Form.h"

namespace nnse
{
	extern io_module io_m;

	// Constructor and Destructor

	template <class T, class U> MLP<T, U>::MLP(solver<T, U>^ p, unsigned int MLP_n)
	: parent(p), number(MLP_n)
	{
		io_m.startup_stream << "Constructing MLP " << get_number() << std::endl;
		put_seed(12);
		put_number_of_iterations(1000);
		put_learning_rate(0.1);
		put_momentum_flag(true);
		put_momentum_value(0.1);
		put_number_of_hidden_layers(1);
		put_layers();
		MLP_form=nullptr;
	}

	template <class T, class U> MLP<T, U>::~MLP()
	{
		io_m.startup_stream << "Destructing MLP " << get_number() << std::endl;
	}

	// Accessor Functions

	template <class T, class U> inline problem<T, U>^ MLP<T, U>::get_parent_problem()
	{
		return get_parent_solver()->get_parent_problem();
	}

	template <class T, class U> inline solver<T, U>^ MLP<T, U>::get_parent_solver()
	{
		return parent;
	}

	template <class T, class U> inline unsigned int MLP<T, U>::get_number()
	{
		return number;
	}

	template <class T, class U> inline void MLP<T, U>::put_number(unsigned int n)
	{
		number=n;
	}

	template <class T, class U> inline unsigned int MLP<T, U>::get_seed()
	{
		return seed;
	}

	template <class T, class U> inline void MLP<T, U>::put_seed(unsigned int s)
	{
		seed=s;
		srand(get_seed());
	}

	template <class T, class U> inline unsigned int MLP<T, U>::get_number_of_iterations()
	{
		return number_of_iterations;
	}

	template <class T, class U> inline void MLP<T, U>::put_number_of_iterations(unsigned int n_i)
	{
		number_of_iterations=n_i;
	}

	template <class T, class U> inline U MLP<T, U>::get_learning_rate()
	{
		return learning_rate;
	}

	template <class T, class U> inline void MLP<T, U>::put_learning_rate(U l_r)
	{
		learning_rate=l_r;
	}

	template <class T, class U> inline bool MLP<T, U>::get_momentum_flag()
	{
		return momentum_flag;
	}

	template <class T, class U> inline void MLP<T, U>::put_momentum_flag(bool m_f)
	{
		momentum_flag=m_f;
	}

	template <class T, class U> inline U MLP<T, U>::get_momentum_value()
	{
		return momentum_value;
	}

	template <class T, class U> inline void MLP<T, U>::put_momentum_value(U m_v)
	{
		momentum_value=m_v;
	}

	template <class T, class U> inline unsigned int MLP<T, U>::get_number_of_hidden_layers()
	{
		return number_of_hidden_layers;
	}

	template <class T, class U> inline unsigned int MLP<T, U>::get_number_of_layers()
	{
		return get_number_of_hidden_layers()+2;
	}

	template <class T, class U> inline void MLP<T, U>::put_number_of_hidden_layers(unsigned int n_h_l)
	{
		number_of_hidden_layers=n_h_l;
	}

	template <class T, class U> inline layer<T, U>% MLP<T, U>::get_layer(int l_n)
	{
		if (l_n<0)
		{
			l_n=0;
		}
		else if (l_n>get_number_of_layers()-1)
		{
			l_n=get_number_of_layers()-1;
		}
		if (layers)
		{
			return *layers[l_n];
		}
		else
		{
			throw std::out_of_range("\nError in MLP<T, U>::get_layer");
		}
	}

	template <class T, class U> void MLP<T, U>::put_layers()
	{
		layers=gcnew array<layer<T, U>^>(get_number_of_layers());
		layers[0]=gcnew input_layer<T, U>(this, 0);
		for (unsigned int l_n=1; l_n<=get_number_of_hidden_layers(); l_n++)
		{
			layers[l_n]=gcnew hidden_layer<T, U>(this, l_n);
		}
		layers[get_number_of_layers()-1]=gcnew output_layer<T, U>(this, get_number_of_layers()-1);
	}

	// Worker Functions

	template <class T, class U> U MLP<T, U>::training()
	{
		io_m.results_stream << "Training:" << std::endl;
		U E=0.0;
		bool update_flag=false;
		unsigned int progress_min=0;
		unsigned int progress_val=0;
		unsigned int progress_max=get_number_of_iterations()*get_parent_problem()->get_number_of_examples();
		for (unsigned int it=0; it<get_number_of_iterations(); it++)
		{
			io_m.results_stream << "Iteration: " << it << std::endl;
			E=0.0;
			for (unsigned int ex=0; ex<get_parent_problem()->get_number_of_examples(); ex++)
			{
				get_parent_problem()->put_current_example(ex);
				io_m.results_stream << "Example: " << ex << std::endl;
				io_m.results_stream << "Forward:" << std::endl;
				forward(update_flag);
				io_m.results_stream << "Reverse:" << std::endl;
				E+=reverse();
				update_flag=true;
				get_parent_solver()->show_vectors();
				progress_val=(it*get_parent_problem()->get_number_of_examples())+ex;
				get_parent_solver()->display_solver_form("Training in progress...", progress_min, progress_val, progress_max);
			}
			io_m.results_stream << "Error: " << E << std::endl;
			io_m.errors_stream << E << std::endl;
		}
		return E;
	}

	template <class T, class U> U MLP<T, U>::testing()
	{
		io_m.results_stream << "Testing:" << std::endl;
		U E=0.0;
		bool update_flag=false;
		unsigned int progress_min=0;
		unsigned int progress_val=0;
		unsigned int progress_max=get_parent_problem()->get_number_of_examples();
		for (unsigned int ex=0; ex<get_parent_problem()->get_number_of_examples(); ex++)
		{
			get_parent_problem()->put_current_example(ex);
			io_m.results_stream << "Example: " << ex << std::endl;
			io_m.results_stream << "Forward:" << std::endl;
			forward(update_flag);
			io_m.results_stream << "Reverse:" << std::endl;
			E+=reverse();
			progress_val=ex;
			get_parent_solver()->show_vectors();
			get_parent_solver()->display_solver_form("Testing in progress...", progress_min, progress_val, progress_max);
		}
		io_m.results_stream << "Error: " << E << std::endl;
		io_m.errors_stream << E << std::endl;
		return E;
	}

	template <class T, class U> void MLP<T, U>::forward(bool update_flag)
	{
		for (unsigned int l_n=0; l_n<get_number_of_layers(); l_n++)
		{
			get_layer(l_n).forward(get_layer(l_n+1), update_flag);
		}
	}

	template <class T, class U> U MLP<T, U>::reverse()
	{
		U E=0.0;
		for (int l_n=get_number_of_layers()-1; l_n>=0; l_n--)
		{
			E+=get_layer(l_n).reverse(get_layer(l_n+1));
		}
		return E;
	}

	// GUI Functions

	template <class T, class U> void MLP<T, U>::display_form()
	{
		if (!MLP_form)
		{
			MLP_form=gcnew SimpleGUI::MLP<T, U>(this);
		}
		MLP_form->Show();
		MLP_form->update_form();
	}

	template <class T, class U> void MLP<T, U>::invalidate_form()
	{
		MLP_form=nullptr;
	}

	template <class T, class U> void MLP<T, U>::display_layers()
	{
		for (unsigned int l_n=0; l_n<get_number_of_layers(); l_n++)
		{
			get_layer(l_n).display_form();
		}
	}

	// Serialisation Functions

	template <class T, class U> void MLP<T, U>::read(std::wifstream& in)
	{
		unsigned int number;
		in >> number;
		put_number(number);
		unsigned int seed;
		in >> seed;
		put_seed(seed);
		unsigned int number_of_iterations;
		in >> number_of_iterations;
		put_number_of_iterations(number_of_iterations);
		U learning_rate;
		in >> learning_rate;
		put_learning_rate(learning_rate);
		bool momentum_flag;
		in >> momentum_flag;
		put_momentum_flag(momentum_flag);
		U momentum_value;
		in >> momentum_value;
		put_momentum_value(momentum_value);
		unsigned int number_of_hidden_layers;
		in >> number_of_hidden_layers;
		put_number_of_hidden_layers(number_of_hidden_layers);
		for (unsigned int l_n=0; l_n<get_number_of_layers(); l_n++)
		{
			in >> get_layer(l_n);
		}
	}

	template <class T, class U> void MLP<T, U>::write(std::wofstream& out)
	{
		out << get_number() << " ";
		out << get_seed() << " ";
		out << get_number_of_iterations() << " ";
		out << get_learning_rate() << " ";
		out << get_momentum_flag() << " ";
		out << get_momentum_value() << " ";
		out << get_number_of_hidden_layers() << " ";
		for (unsigned int l_n=0; l_n<get_number_of_layers(); l_n++)
		{
			out << get_layer(l_n);
		}
	}

	template <class T, class U> void MLP<T, U>::display(std::wofstream& out)
	{
		out << "MLP:" << std::endl;
		out << "Number: " << get_number() << std::endl;
		out << "Seed: " << get_seed() << std::endl;
		out << "Number of Iterations: " << get_number_of_iterations() << std::endl;
		out << "Learning Rate: " << get_learning_rate() << std::endl;
		out << "Momentum Flag: " << get_momentum_flag() << std::endl;
		out << "Momentum Value: " << get_momentum_value() << std::endl;
		out << "Number of Hidden Layers: " << get_number_of_hidden_layers() << std::endl;
		for (unsigned int l_n=0; l_n<get_number_of_layers(); l_n++)
		{
			out << get_layer(l_n);
		}
	}

	template <class T, class U> std::wifstream& operator>>(std::wifstream& in, MLP<T, U>% mlp)
	{
		if (in.good())
		{
			mlp.read(in);
			return in;
		}
		else
		{
			throw std::ios_base::failure("\nError in wifstream& >> MLP<T, U>%");
		}
	}

	template <class T, class U> std::wofstream& operator<<(std::wofstream& out, MLP<T, U>% mlp)
	{
		if (out.good())
		{
			mlp.write(out);
			return out;
		}
		else
		{
			throw std::ios_base::failure("\nError in wofstream& << MLP<T, U>%");
		}
	}

	// Explicit Instantiation

	template ref class MLP<bool, double>;
	template ref class MLP<unsigned char, double>;
	template std::wifstream& operator>>(std::wifstream& in, MLP<bool, double>% mlp);
	template std::wifstream& operator>>(std::wifstream& in, MLP<unsigned char, double>% mlp);
	template std::wofstream& operator<<(std::wofstream& out, MLP<bool, double>% mlp);
	template std::wofstream& operator<<(std::wofstream& out, MLP<unsigned char, double>% mlp);
}
